/*
 * TowerControlController.cpp
 *
 *  Created on: Sep 22, 2013
 *      Author: gonprieto
 */

#include "TowerControlController.h"
#include "../utils/SimulatorUtils.h"
#include "FlightState.h"

TowerControlController::TowerControlController(unsigned int controllersCount,unsigned int runwayCount) :
		inbox(SimulatorUtils::getTowerInboxName(),true) {

	this->isRunning = true;
	this->runwaysCount = runwayCount;
	this->controllersCount = controllersCount;

	for (unsigned int i = 0; i < controllersCount; i++) {
		FlightControllerInbox* inboxFC = new FlightControllerInbox(SimulatorUtils::getFlighControlerInboxName(i));

		inboxFC->crear();
		inboxFC->openForWrite();

		this->flightControllersInbox.push_back(inboxFC);
	}

	int valoresiniciales[runwayCount];
	for (unsigned int i = 0; i < runwayCount; ++i) {
		valoresiniciales[i] = 1;
	}

	this->semRunways = new Semaforo(SimulatorUtils::getRunwayName().c_str(), runwayCount);
	this->semRunways->inicializar(valoresiniciales);
}

TowerControlController::~TowerControlController() {
	log << "TowerControlController::~TowerControlController" << eol;
	std::vector<FlightControllerInbox*>::iterator controlesInboxIt;
	for (controlesInboxIt = flightControllersInbox.begin();
			controlesInboxIt != flightControllersInbox.end();
			controlesInboxIt++) {

		log << "TowerControlController::~TowerControlController - Cierro pid:" << getpid() <<eol;

		(*controlesInboxIt)->cerrar();

		//Elimino la referencia
		delete *controlesInboxIt;
	}
	inbox.eliminar();

	delete this->semRunways;
}

void TowerControlController::finalizarOperaciones() {
	log << "TowerControlController::finalizarOperaciones - Termino el proceso" << eol;

	//Cambio el estado a parado
	this->isRunning = false;

	//les comunico a todos los controlers que terminen operaciones
	for (unsigned int i = 0; i < controllersCount; i++) {
		flightControllersInbox[i]->endShift(i);
	}
}

int TowerControlController::run() {
	DTO* dto =NULL;

	log << "TowerControlController::run - Inicio TowerControlController pid:"<< getpid() <<eol;

	while (this->isRunning || tengoProcesosOcupadas()) {
		log << "TowerControlController::run - Leo mensaje a procesar" << eol;

		dto = inbox.leerDTO();

		if(dto==NULL){
			log << "FlightController::run - Termino el FlightControler" << eol;
			return 0;
		}

		log << "TowerControlController::run - Lei mensaje a procesar" << eol;

		procesarMensaje(dto);

		delete dto;
	}

	log << "TowerControlController::run - Salgo de TowerControlController" << eol;

	//Cierro y elimino el fifo de inbox.
	inbox.cerrar();

	log << "TowerControlController::run - Termino el TowerControlController ok" << eol;

	if(tengoProcesosOcupadas()){
		return -1;
	}

	return (0);
}

bool TowerControlController::tengoProcesosOcupadas(){

	for (unsigned int i = 0; i < this->runwaysCount; i++) {
		if ( this->semRunways->getValor(i) == 0){
			log << "TowerControlController::tengoProcesosOcupadas - Tengo pistas ocupadas" << eol;
			return true;
		}
	}

	return false;
}

void TowerControlController::procesarMensaje(DTO* dto) {
	log << "TowerControlController::procesarMensaje - Procesando mensaje tipo : " << dto->getType() << eol;

	switch (dto->getType()) {
	case RUNWAY_RELATED:{
		log << "TowerControlController::procesarMensaje - Procesando RUNWAY_RELATED" << eol ;
		procesarMensajeRunway((DTORunwayRelated*) dto);
		break;
	}
	case FLIGHT_RELATED:{
		log << "TowerControlController::procesarMensaje - Procesando FLIGHT_RELATED  " << eol;
		procesarVuelo((DTOFlightRelated*) dto);
		break;
	}
	case END_GENERADOR:{
		log << "TowerControlController::procesarMensaje - Procesando END_GENERADOR " << eol ;
		finalizarOperaciones();
		break;
	}
	case FLIGHT_CONTROLER_REQUEST:
	case FLIGHT_CONTROLER:
	case FLIGHT:
	default:
		log << "ERROR: No se pudo identificar el tipo de mensaje";
		break;
	}
}

void TowerControlController::procesarMensajeRunway(DTORunwayRelated* dto) {
	log << "TowerControlController::procesarMensajeRunway - Libero runway id: " << dto->getRunwayId() << eol;

	this->semRunways->v(dto->getRunwayId());

	tengoProcesosOcupadas();
}

void TowerControlController::procesarVuelo(DTOFlightRelated* dto) {
	log << "TowerControlController::procesarVuelo - Action to perfom Id:" << dto->getActionToPerform() << eol;
	//otengo un controlador aereo y le paso el mensaje
	int controlador = getFlightControllerToUse();

	Status flightStatus = (Status) dto->getDtoFlight().getState();
	if (flightStatus == IN_THE_AIR) {
		log << "TowerControlController::procesarVuelo - Vuelo en el aire, asigno controlador id: " << controlador << "-FlightId:"<< dto->getDtoFlight().getFlightId() << eol;

		DTOFlightToLand toLand = DTOFlightToLand(dto->getDtoFlight());
		this->flightControllersInbox[controlador]->landFlight(toLand);
	} else if (flightStatus == PARKED) {
		log << "TowerControlController::procesarVuelo - Vuelo estacionado, asigno controlador id: " << controlador << "-FlightId:"<< dto->getDtoFlight().getFlightId() << eol;

		DTOFlightToTakeOff toTakeOff = DTOFlightToTakeOff(dto->getDtoFlight());
		this->flightControllersInbox[controlador]->takeOffFlight(toTakeOff);
	}

}


int TowerControlController::getFlightControllerToUse(){
	//otengo una pista y la asigno para la operacion
	int flightControllerId= SimulatorUtils::random_in_range(this->controllersCount);

	log << "TowerControlController::getFlightControllerToUse - FlightController a usar- id: " << flightControllerId << eol;
	return flightControllerId;
}

